home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
heapdbg.zip
/
HEAP.DBG
< prev
Wrap
Text File
|
1990-01-17
|
22KB
|
720 lines
*********************************README.1ST**********************************
Here is a list of the files following this introduction:
MAIN.C : A sample program to demonstrate the heap debugging stuff
HEAP.H : The header file that turns on the debugging stuff
HEAP.C : The routines that comprise the heap debugger
SCREEN.C : The routines to dump debugging information to the screen.
This is the code that is machine/operating system dependent.
_HEAP.H : A private header file for the debugging routines.
*********************************MAIN.C*****************************************
/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This is the main() module used to test the heap
* debugger routines. It does a few different
* things that should be detected by the debugger
* routines.
*/
#include <stdlib.h>
#include <string.h>
#include "heap.h"
void main( void )
{
char *test_guy_1;
char *test_guy_2;
char *test_guy_3;
char *maverick_pointer;
test_guy_1 = malloc( 1000 );
test_guy_2 = malloc( 2000 );
/*
* This call to strdup creates a maverick pointer that I should detect
* next time I get into my debugger routines.
*/
maverick_pointer = strdup( "This is a test" );
test_guy_3 = malloc( 3000 );
/*
* Here I write one byte past my allocated area. This should create a
* picket error.
*/
test_guy_2[ 2000 ] = 'X';
free( test_guy_1 );
free( maverick_pointer );
free( test_guy_2 );
free( test_guy_3 );
}
*********************************HEAP.H*****************************************
#define malloc(size) my_malloc(__FILE__,__LINE__,(size))
#define calloc(nitems,size) my_calloc(__FILE__,__LINE__,(nitems),(size))
#define free(block) my_free(__FILE__,__LINE__,(block))
void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void *my_calloc(char *file_name,unsigned int line_number,size_t nitems,size_t size);
void my_free(char *file_name,unsigned int line_number, void *block);
/* Prototypes from SCREEN.C */
void draw_boxes_and_titles(char *labels[],int row_count);
void screenputc(int row,int col,char c);
void screenputf(int row,int col,char *format,...);
void screenputs(int row,int col,char *string);
void screenclearline(int row);
void initialize_screen(char *labels[],int widths[]);
void setup_screen(char *labels[],int widths[],int rows);
void restore_screen(void);
/* Prototypes from HEAP.C */
void screen_message(int immediate_return, int tag, char *format,...);
void hide_screen(void);
void my_free(char *file_name,unsigned int line_number, void *block);
void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void initialize_tags(void);
void display_tag_table(int last_tag);
void add_tag_to_table(char far *pointer,char maverick,size_t size,char *file_name,unsigned int line_number);
void delete_tag_from_table(int tag);
void verify_heap(void);
void heap_walk(void);
*********************************HEAP.C*****************************************
/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This module contains the replacement routines used
* to debug heap problems. The routines are used in
* conjunction with the HEAP.H header file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <conio.h>
#include "_heap.h"
/*
* This structure defines all the fields that I use in the tag
* table database.
*/
#define TAG_MAX 100
struct tags {
char far * returned_address;
char far * real_address;
size_t size;
char *file_name;
unsigned int line_number;
char maverick;
char tagged;
} tag_table[TAG_MAX];
int next_free_tag = -1;
/*
* These are some odds and ends I use all around.
*/
char leading_picket[] = "0123456789ABCDE";
char trailing_picket[] = "FEDCBA987654321";
#define PICKET_SIZE sizeof( leading_picket )
/*
* These are the labels and widths for each of the columns in the output
* screen.
*/
char *labels[]={"Tag #","File Name","Line #","Picket","Address","Size","Picket",""};
int widths[]={5,12,6,15,9,6,15};
extern int columns[];
int screen_up=0;
int message_line;
/*
* This is the my_malloc routine that replaces malloc(). Before it does
* anything else, it performs the heap checkout stuff. This will pop up
* a message if anything funny is detected. It then gets the pointer
* for the caller, and adds it to the tag table. The screen is then
* cleared up, and the pointer is returned.
*/
void *my_malloc(char *file_name,unsigned int line_number,size_t size)
{
void *malloc_pointer;
size_t real_size;
verify_heap();
real_size = size + PICKET_SIZE*2;
malloc_pointer = malloc( real_size );
if ( malloc_pointer == NULL ) {
screen_message( 1, 0, "File: %s Line: %u requesting %u bytes",
file_name, line_number, size );
screen_message( 0, 0, "Malloc failed! Null pointer will be returned." );
}
else
add_tag_to_table( malloc_pointer, 0, size, file_name, line_number );
hide_screen();
return( ( char * ) malloc_pointer + PICKET_SIZE );
}
/*
* my_free is set up to replace free(). Just like my_malloc(), it first
* checks out the heap and prints a message if anything funny shows up.
* Before I try to free the block, I have to check and see if it is in
* my tag table. If it is, I free the real pointer, not the one I passed
* back to the caller. If it isn't in the tag table, I print a message
* out to that effect, and return.
*/
void my_free( char *file_name, unsigned int line_number, void *block )
{
int tag;
verify_heap();
for ( tag = 0; tag < TAG_MAX ; tag++ ) {
if ( tag_table[ tag ].returned_address == ( void far * ) block )
break;
}
if ( tag < TAG_MAX ) {
if ( tag_table[ tag ].maverick ) {
screen_message( 1, 0, "File: %s Line: %u freeing block %Fp",
file_name, line_number, ( void far * ) block );
screen_message( 0, 0, "Tag is a maverick entry!" );
free( block );
}
else
free( ( char * ) block - PICKET_SIZE );
delete_tag_from_table( tag );
}
else {
screen_message( 1, 0, "File: %s Line: %u freeing block %Fp",
file_name, line_number, ( void far * ) block );
screen_message( 0, 0, "Tag was not found in tag table! Going to try and free() it." );
free( block );
screen_message( 0, 0, "Heap after freeing anonymous block!" );
}
hide_screen();
}
/*
* I need to initialize the tag table my first time through. This
* routine gets called all the time, but only performs the initialization
* once, when next_free_tag is -1.
*/
void initialize_tags()
{
int i;
if ( next_free_tag == -1 ) {
next_free_tag = 0;
for ( i = 0 ; i < TAG_MAX ; i++ ) {
tag_table[ i ].returned_address = NULL;
tag_table[ i ].file_name = "Not in use";
tag_table[ i ].line_number = 0;
tag_table[ i ].size = 0;
}
}
}
/*
* This is the routine called to display the tag table when something
* has gone wrong. It sits in a loop displaying tag table entries, 15
* at a time. The user can hit the 'u' or 'd' keys to move up or down
* in the table. Any other key breaks the user out.
*/
void display_tag_table( int last_tag )
{
int first_tag;
int offset;
int tag;
char far *picket_pointer;
int key;
if ( last_tag < 16 )
first_tag = 0;
else
first_tag = last_tag - 15;
for ( ; ; ) {
for ( offset = 0 ; offset < 15 ; offset++ ) {
tag = first_tag + offset;
screenputf( offset + 3, columns[ 0 ] + 1, "%-3d", tag );
screenputf( offset + 3, columns[ 1 ] + 1, "%-12s",
tag_table[ tag ].file_name );
screenputf( offset + 3, columns[ 2 ] + 1, "%-5d",
tag_table[ tag ].line_number );
if ( tag_table[ tag ].returned_address != NULL ) {
picket_pointer = tag_table[ tag ].returned_address;
picket_pointer -= PICKET_SIZE ;
if ( tag_table[ tag ].maverick )
screenputf( offset + 3, columns[ 3 ] + 1,"%15s", "***MAVERICK***" );
else
screenputf( offset + 3, columns[ 3 ] + 1, "%15s", picket_pointer );
screenputf( offset + 3, columns[ 4 ] + 1, "%Fp",
tag_table[ tag ].returned_address );
picket_pointer += PICKET_SIZE;
picket_pointer += tag_table[ tag ].size;
if ( tag_table[ tag ].maverick )
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", "***MAVERICK***" );
else
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", picket_pointer );
}
else {
screenputf( offset + 3, columns[ 3 ] + 1, "%15s", "" );
screenputf( offset + 3, columns[ 4 ] + 1, " NULL " );
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", "" );
}
screenputf( offset + 3, columns[ 5 ] + 1, "%-5d", tag_table[ tag ].size );
}
key = getch();
if ( key == 'u' || key == 'U' ) {
first_tag -= 15;
if (first_tag < 0)
first_tag = 0;
}
else if ( key == 'd' || key == 'D' ) {
first_tag += 15;
if ( ( first_tag + 15 ) >= TAG_MAX )
first_tag = TAG_MAX - 15;
}
else
break;
}
}
/*
* This routine is called when a new pointer needs to be added to the tag
* table. It can be a maverick pointer or a regular pointer.
*/
void add_tag_to_table( char far *pointer,
char maverick,
size_t size,
char *file_name,
unsigned int line_number )
{
int i;
if ( next_free_tag >= TAG_MAX )
return;
tag_table[ next_free_tag ].returned_address = pointer;
tag_table[ next_free_tag ].real_address = pointer;
tag_table[ next_free_tag ].size = size;
tag_table[ next_free_tag ].file_name = file_name;
tag_table[ next_free_tag ].line_number = line_number;
tag_table[ next_free_tag ].maverick = maverick;
if ( !maverick ) {
for ( i = 0 ; i < PICKET_SIZE ; i++ )
pointer[ i ] = leading_picket[ i ];
pointer += size;
pointer += PICKET_SIZE ;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
pointer[ i ] = trailing_picket[ i ];
tag_table[ next_free_tag ].returned_address += PICKET_SIZE;
}
next_free_tag++;
}
/*
* This routine is called when a tag needs to be deleted from the table. This
* can happen when a call to free() is made, or when a heapwalk shows that
* one of the pointers is missing.
*/
void delete_tag_from_table( int tag )
{
int i;
next_free_tag--;
for ( i = tag ; i < next_free_tag ; i++ )
tag_table[ i ] = tag_table[ i + 1 ];
tag_table[ i ].returned_address = NULL;
tag_table[ i ].file_name = "Not in use";
tag_table[ i ].line_number = 0;
tag_table[ i ].size = 0;
}
/*
* This is the verify routine that is called on the entry to my_malloc()
* or my_free(). It calls the heap_walk() routine first, to let it do
* its check out of the heap. It then goes through the entire tag table
* and verifies that the pickets are all intact.
*/
void verify_heap()
{
int i;
int tag;
char far *picket_pointer;
initialize_tags();
heap_walk();
for ( tag = 0 ; tag < next_free_tag ; tag++ ) {
if ( tag_table[ tag ].maverick )
continue;
picket_pointer = tag_table[ tag ].returned_address;
picket_pointer -= PICKET_SIZE ;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
if ( picket_pointer[ i ] != leading_picket[ i ] ) {
screen_message( 0, i, "Error in leading picket, tag %3d", tag );
break;
}
picket_pointer += PICKET_SIZE ;
picket_pointer += tag_table[tag].size;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
if ( picket_pointer[ i ] != trailing_picket[ i ] ) {
screen_message( 0, tag, "Error in trailing picket, tag %3d", tag );
break;
}
}
}
/*
* This is the routine that walks through the heap. If an heap entry
* is not found in the tag table, a message is printed, and it is added
* as a maverick. Otherwise, the tag is noted. After walking through
* the whole heap, a check is done to see if any of our tagged entries
* didn't show up, which generates another message.
*
* Remember that this code is MSC-specific. You need to rewrite this
* routine for every different compiler.
*/
void heap_walk()
{
struct _heapinfo hinfo;
int heapstatus;
size_t size;
int i;
hinfo._pentry = NULL;
for ( i = 0 ; i < next_free_tag ; i++ )
tag_table[ i ].tagged = 0;
for ( ; ; ) {
heapstatus = _heapwalk( &hinfo );
if ( heapstatus == _HEAPEMPTY )
break;
if ( heapstatus == _HEAPEND )
break;
if ( heapstatus != _HEAPOK ) {
screen_message( 0, 0, "Heap is corrupted! Going to exit." );
exit( 1 );
}
if ( hinfo._useflag != _USEDENTRY )
continue;
for ( i = 0 ; i < next_free_tag ; i++ ) {
if ( (int far *) tag_table[ i ].real_address == hinfo._pentry ) {
tag_table[ i ].tagged = 1;
break;
}
}
if ( i == next_free_tag ) {
size = hinfo._size;
if ( i < TAG_MAX )
tag_table[ i ].tagged = 1;
add_tag_to_table( (char far *) hinfo._pentry, 1, size, "MAVERICK",0 );
screen_message( 0, i, "Found a maverick heap entry: %Fp", hinfo._pentry );
}
}
/*
* At this point every entry should have been tagged, so I can go through
* the table and look for ones that didn't get tagged.
*/
for ( i = 0 ; i < next_free_tag ; i++ ) {
if ( tag_table[ i ].tagged == 0 ) {
screen_message( 0, i, "Didn't find heap entry in heapwalk, tag %d", i );
delete_tag_from_table( i );
}
}
}
/*
* During the process of checking out the heap, if I see anything worthy
* of a message, I call this routine. If the screen is not already up,
* this guy pulls it up. Then it prints the message.
*/
void screen_message( int immediate_return, int tag, char *format, ... )
{
char message[ 81 ];
va_list args;
if ( screen_up == 0 ) {
screen_up = 1;
message_line = 20;
setup_screen( labels, widths, 15 );
}
va_start( args, format );
vsprintf( message, format, args );
screenputs( message_line, 0, message );
if ( ++message_line >= 25 )
message_line = 20;
if ( !immediate_return )
display_tag_table( tag );
}
/*
* After all the work is done, I have to hide my heap screen so the user
* can see the application output. This is pretty easy to do, a routine
* in SCREEN.C does the whole job for me.
*/
void hide_screen()
{
if ( screen_up != 0 ) {
screen_up = 0;
restore_screen();
}
}
*********************************SCREEN.C***************************************
/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This module contains the screen I/O routines used in
* the heap debugger module. These are very simplified
* screen I/O routines.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "_heap.h"
/*
* These are all the line drawing constants defined.
*/
#define UL_CORNER 218
#define UR_CORNER 191
#define LL_CORNER 192
#define LR_CORNER 217
#define UPPER_TEE 194
#define LOWER_TEE 193
#define LEFT_TEE 195
#define RIGHT_TEE 180
#define CENTER_TEE 197
#define HORIZONTAL_LINE 196
#define VERTICAL_LINE 179
/*
* I create a structure so I can write directly to screen as if it were
* a big array. That way the compiler takes care of computing the
* addresses, all I have to do is insert the row and column.
*/
struct video_element {
unsigned char character;
unsigned char attribute;
};
struct video_element (far *physical_screen)[25][80];
struct video_element saved_screen[25][80];
/*
* This routine draws the box I use up on the screen. It is passed a
* list of labels to draw at the head of the columns, plus a count
* of how many rows are to be left open for data entry. It depends
* on some earlier code somewhere to have initialized an array called
* columns[] that tells it where to draw each column on the screen.
* There is a lot of code here to draw the boxes, but it is all
* straightforward.
*/
int columns[10];
int column_count=0;
void draw_boxes_and_titles(char *labels[],int row_count)
{
int col;
int row;
int i;
int j;
int rows[3];
/*
* The three rows I define are the top and bottom of the box, plus the
* line that divides the title lines from the data lines.
*/
rows[0]=0;
rows[1]=2;
rows[2]=3+row_count;
for (col=1;col<columns[column_count];col++)
for (i=0;i<3;i++)
(*physical_screen)[rows[i]][col].character = HORIZONTAL_LINE;
for (i=0;i<=column_count;i++)
for (row=0;row<(row_count+4);row++)
(*physical_screen)[row][columns[i]].character = VERTICAL_LINE;
(*physical_screen)[0][columns[0]].character = UL_CORNER;
(*physical_screen)[row_count+3][columns[0]].character = LL_CORNER;
(*physical_screen)[0][columns[column_count]].character = UR_CORNER;
(*physical_screen)[row_count+3][columns[column_count]].character = LR_CORNER;
(*physical_screen)[rows[1]][columns[0]].character = LEFT_TEE;
(*physical_screen)[rows[1]][columns[column_count]].character = RIGHT_TEE;
for (j=1;j<column_count;j++)
(*physical_screen)[0][columns[j]].character = UPPER_TEE;
for (j=1;j<column_count;j++)
(*physical_screen)[row_count+3][columns[j]].character = LOWER_TEE;
for (j=1;j<column_count;j++)
(*physical_screen)[rows[1]][columns[j]].character = CENTER_TEE;
/*
* Here is where I draw the labels. They need to go in the center of
* their little boxes.
*/
for (i=0;i<column_count;i++)
{
col=columns[i]+1;
col += (columns[i+1]-columns[i]-1)/2;
col -= strlen(labels[i])/2;
screenputs(1,col,labels[i]);
}
}
/*
* This is a general purpose routine to print a formatted string on
* the screen.
*/
void screenputf(int row,int col,char *format,...)
{
char buffer[81];
va_list args;
va_start(args,format);
vsprintf(buffer,format,args);
screenputs(row,col,buffer);
}
/*
* This is a general purpose routine to put an unformatted string
* out to the screen.
*/
void screenputs(int row,int col,char *string)
{
char c;
while (1)
{
c=*string++;
if (c=='\0')
break;
(*physical_screen)[row][col++].character=c;
}
}
/*
* This is a general purpose routine to clear a whole line on the
* screen.
*/
void screenclearline(int row)
{
int col;
for (col=0;col<80;col++)
(*physical_screen)[row][col].character=' ';
}
/*
* This is the screen initialization code. It is a trap door routine that
* gets called all the time, but only executes once. It computes what
* columns the vertical lines are going to go in, based on the widths needed
* for each column, passed as a parameter.
* Note that if you are using a monochrome monitor, you need to change
* the screen pointer to be 0xb0000000L.
* This routine also initializes the tag table
*/
void initialize_screen(char *labels[],int widths[])
{
int row;
int col;
int i;
static int first_time=0;
if (first_time==0)
{
first_time=1;
columns[0]=1;
column_count=0;
while (strlen(labels[column_count]) != 0)
{
columns[column_count+1] = columns[column_count]+widths[column_count]+1;
column_count++;
}
}
physical_screen=(struct video_element (far *)[25][80])0xb8000000L;
for (row=0;row<25;row++)
for (col=0;col<80;col++)
{
saved_screen[row][col]=(*physical_screen)[row][col];
(*physical_screen)[row][col].character=' ';
(*physical_screen)[row][col].attribute=0x1b;
}
}
/*
* Whenever the heap routines decide they need to print a screen message,
* they set up the screen first by calling this guy.
*/
void setup_screen(char *labels[],int widths[],int rows)
{
initialize_screen(labels,widths);
draw_boxes_and_titles(labels,rows);
}
/*
* After the heap routines are done printing debug information, they
* have to restore the screen back to where it was when they got called.
* This routine does that.
*/
void restore_screen()
{
int row;
int col;
for (row=0;row<25;row++)
for (col=0;col<80;col++)
(*physical_screen)[row][col]=saved_screen[row][col];
}
*********************************_HEAP.H****************************************
/* Prototypes from SCREEN.C */
void draw_boxes_and_titles(char *labels[],int row_count);
void screenputc(int row,int col,char c);
void screenputf(int row,int col,char *format,...);
void screenputs(int row,int col,char *string);
void screenclearline(int row);
void initialize_screen(char *labels[],int widths[]);
void setup_screen(char *labels[],int widths[],int rows);
void restore_screen(void);
/* Prototypes from HEAP.C */
void screen_message(int immediate_return, int tag, char *format,...);
void hide_screen(void);
void my_free(char *file_name,unsigned int line_number, void *block);
void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void initialize_tags(void);
void display_tag_table(int last_tag);
void add_tag_to_table(char far *pointer,char maverick,size_t size,char *file_name,unsigned int line_number);
void delete_tag_from_table(int tag);
void verify_heap(void);
void heap_walk(void);